
#include <BSpace>
#include <BStream>
#include <BNode>
#include <BPhysics>

#include "member_BNode.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BNode*)_ptr)
#define member_allocate()		_ptr = new member_BNode(this)
#define member_release()		delete (member_BNode*)_ptr

BNode::BNode(const BString& name) : BObject(name)
{
	member_allocate();
}
BNode::BNode()
{
	member_allocate();
}
BNode::~BNode()
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		member.disconnect(shape);
	}
	member.shapes.clear();
	member_release();
}

void BNode::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (visible)
			emit(Signal_Show);
		else
			emit(Signal_Hide);
		emit(Signal_Fresh);
	}
}
bool BNode::visible() const
{
	return member.visible;
}

void BNode::move(const BVector& move)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + move);
	setMatrix(matrix);
}
void BNode::move(float x, float y, float z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + BVector(x, y, z));
	setMatrix(matrix);
}

void BNode::setPosition(const BVector& position)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(position);
	setMatrix(matrix);
}
void BNode::setPosition(float x, float y, float z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(x, y, z);
	setMatrix(matrix);
}
BVector BNode::position() const
{
	return member.matrix.position();
}

void BNode::setRotate(float rx, float ry, float rz)
{
	BQuater xrotate(rx, BVector(1, 0, 0));
	BQuater yrotate(ry, BVector(0, 1, 0));
	BQuater zrotate(rz, BVector(0, 0, 1));
	BMatrix matrix = member.matrix;
	matrix.setRotate(xrotate * yrotate * zrotate);
	setMatrix(matrix);
}
void BNode::setRotate(const BQuater& quater)
{
	BMatrix matrix = member.matrix;
	matrix.setRotate(quater);
	setMatrix(matrix);
}
BQuater BNode::rotate() const
{
	return member.matrix.rotate();
}

void BNode::setScale(const BVectorf& scale)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(scale);
	setMatrix(matrix);
}
void BNode::setScale(float x, float y, float z)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(x, y, z);
	setMatrix(matrix);
}
BVector BNode::scale() const
{
	return member.matrix.scale();
}

void BNode::setMatrix(const BMatrix& matrix)
{
	member.matrix = matrix;
	member.dirtySpace = true;
	emit(Signal_Dirty);
}
const BMatrix& BNode::matrix() const
{
	return member.matrix;
}

const BBox& BNode::box() const
{
	if (member.dirtyBox)
	{
		BSpace space;
		for (int i = 0; i < member.shapes.size(); i++)
		{
			BShape* shape = member.shapes[i];
			space.expand(shape->space());
		}
		BVector size = space.max() - space.min();
		float hx = size.x() * 0.5f;
		float hy = size.y() * 0.5f;
		float hz = size.z() * 0.5f;
		member.box.veca().set(hx, 0.0f, 0.0f);
		member.box.vecb().set(0.0f, hy, 0.0f);
		member.box.vecc().set(0.0f, 0.0f, hz);
		member.box.center() = space.center();
		member.dirtyBox = false;
	}
	return member.box;
}
const BSpace& BNode::space() const
{
	if(member.dirtySpace)
	{
		BBox box = this->box() * member.matrix;
		member.space.reset(box.center());

		member.space.expand(box.v0());
		member.space.expand(box.v1());
		member.space.expand(box.v2());
		member.space.expand(box.v3());

		member.space.expand(box.v4());
		member.space.expand(box.v5());
		member.space.expand(box.v6());
		member.space.expand(box.v7());

		member.dirtySpace = false;
	}
	return member.space;
}

void BNode::setPhysics(BPhysics* physics)
{
	if (member.physics != physics)
	{
		member.physics = physics;
	}
}
BPhysics* BNode::physics() const
{
	return member.physics;
}

void BNode::addShape(BShape* shape)
{
	if (!shape)
		return;
	if (!member.shapes.contain(shape))
	{
		member.shapes.append(shape);
		member.connect(shape, Signal_Fresh, &member_BNode::slotShapeFresh);
		member.connect(shape, Signal_Dirty, &member_BNode::slotShapeDirty);
		member.dirtySpace = true;
		emit(Signal_Dirty);
	}
}
bool BNode::removeShape(BShape* shape)
{
	if (member.shapes.contain(shape))
	{
		BShapeHolder holder = shape;
		member.shapes.remove(shape);
		member.disconnect(shape);
		member.dirtySpace = true;
		emit(Signal_Dirty);
		return true;
	}
	return false;
}
void BNode::clearShapes()
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		member.disconnect(shape);
	}
	member.shapes.clear();
	member.dirtySpace = true;
	emit(Signal_Dirty);
}

bool BNode::contains(const BShape* shape) const
{
	return member.shapes.contain(shape);
}

int BNode::shapeCount() const
{
	return member.shapes.size();
}
BShape* BNode::shape(int index)
{
	return member.shapes(index);
}
const BShape* BNode::shape(int index) const
{
	return member.shapes(index);
}

BShape* BNode::shape(const BString& name)
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}
const BShape* BNode::shape(const BString& name) const
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}
